import serial
import re
from collections import deque
import csv
from datetime import datetime
import threading
import time


class SerialAccelParser:
    def __init__(self, port='COM32', baudrate=921600, maxlen=256):
        # 初始化串口连接
        self.buffer_lock = threading.Lock()
        self.ser = serial.Serial(
            port=port,
            baudrate=baudrate,
            timeout=1  # 非阻塞读取
        )
        # 数据验证正则表达式（支持科学计数法）
        self.pattern = re.compile(
            r'^\*ACCEL,\s*'  # 匹配起始符和可能存在的空格
            r'([+-]?\d+\.?\d*)\s*,\s*'  # X轴（允许数值前后空格）
            r'([+-]?\d+\.?\d*)\s*,\s*'  # Y轴
            r'([+-]?\d+\.?\d*)\s*,\s*'  # Z轴
            r'&\r?\n?$'  # 匹配可能的\r\n结尾（兼容不同系统）
        )
        # 数据缓冲区（支持滑动窗口）
        self.buffer = {
            'X': deque(maxlen=maxlen),
            'Y': deque(maxlen=maxlen),
            'Z': deque(maxlen=maxlen)
        }

    def _parse_line(self, line: str):
        """核心解析逻辑"""
        # 去除首尾空白字符
        cleaned = line.strip()
        # 正则匹配
        match = self.pattern.match(cleaned)
        if match:
            try:
                # 转换为浮点数并存储
                x = float(match.group(1))
                y = float(match.group(2))
                z = float(match.group(3))
                with self.buffer_lock:
                    self.buffer['X'].append(x)
                    self.buffer['Y'].append(y)
                    self.buffer['Z'].append(z)
                return True
            except ValueError:
                print(f"数值转换失败: {cleaned}")
                return False
        else:
            print(f"格式不匹配: {cleaned}")
            return False

    def start_streaming(self, max_retries=3):
        """持续读取并处理数据"""
        retries = 0
        while retries < max_retries:
            try:
                self.open()
                try:
                    while self.ser.is_open:
                        try:
                            raw = self.ser.readline()
                            line = raw.decode('utf-8', errors='ignore')
                            self._parse_line(line)
                        except serial.SerialException as e:
                            print(f"串口通信错误: {str(e)}")
                            break
                except KeyboardInterrupt:
                    print("用户终止操作")
                finally:
                    self.ser.close()
            except serial.SerialException:
                retries += 1
                print(f"尝试重新连接 ({retries}/{max_retries})")
                time.sleep(2)

    def open(self):
        """显式打开串口连接"""
        if not self.ser.is_open:
            self.ser.open()

    def close(self):
        """显式关闭串口连接"""
        if self.ser.is_open:
            self.ser.close()
            print("串口已断开")

class DataLogger(SerialAccelParser):
    def __init__(self, logfile=None, time_fmt="%Y%m%d_%H%M%S", **kwargs):
        super().__init__(**kwargs)
        self.logfile = logfile
        self.time_fmt = time_fmt
        self._log = None  # 文件对象
        self._writer = None  # CSV写入器
        self._lock = threading.Lock()  # 线程锁
        self._is_open = False  # 状态标志

    def open(self):
        """显式打开日志文件并初始化写入器"""
        super().open()
        with self._lock:
            if not self._is_open:
                # 动态生成文件名（若未指定）
                timestamp = datetime.now().strftime(self.time_fmt)
                self.logfile = f'accel_{timestamp}.csv'
                # 打开文件（追加模式，行缓冲优化性能）
                self._log = open(self.logfile, 'a', newline='', buffering=1)
                self._writer = csv.writer(self._log)
                # 写入表头（仅新文件需要）
                if self._log.tell() == 0:
                    self._writer.writerow(['timestamp', 'X', 'Y', 'Z'])
                self._is_open = True

    def close(self):
        """显式关闭日志文件并释放资源"""
        with self._lock:
            if self._is_open and self._log:
                try:
                    self._log.flush()  # 强制刷写缓冲区
                    self._log.close()
                    print(f"日志文件已关闭: {self.logfile}")
                except IOError as e:
                    print(f"文件关闭异常: {str(e)}")
                finally:
                    self._is_open = False
                    self._log = None
                    self._writer = None

    def _parse_line(self, line):
        """解析数据并写入日志（线程安全）"""
        if super()._parse_line(line):
            with self._lock:
                if self._is_open:
                    try:
                        timestamp = datetime.now().isoformat()
                        self._writer.writerow([
                            timestamp,
                            self.buffer['X'][-1],
                            self.buffer['Y'][-1],
                            self.buffer['Z'][-1]
                        ])
                        return True
                    except (OSError, ValueError) as e:
                        print(f"写入失败: {str(e)}")
                        self.close()  # 异常时自动关闭
        return False

    def __del__(self):
        """析构时自动关闭文件（兜底逻辑）"""
        self.close()


# 使用示例
if __name__ == "__main__":
    parser = DataLogger()
    parser.open()
    parser.start_streaming()
